
import javax.swing.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

public class e2BoardPane extends JPanel implements MouseListener,MouseMotionListener {

    private static final long serialVersionUID = 42L;
    e2Applet topapplet;
    // Variables for double buffering
    Image myimage;
    Graphics buffer;
    int panewidth;
    int paneheight;
    int boardstartx;
    int boardstarty;
    int piecewidth;
    int pieceheight;
    int rownb;
    int colnb;
    int i, j;
	int mouseX, mouseY;
	int prevmouseX, prevmouseY;
    Image hiddenimage;
    Graphics hiddengraphics;
	boolean incrementalpaint;
	int count;
	e2colorpattern temppattern;
	
	public void restart_board() {
        piecewidth = topapplet.param.piece_w;
        pieceheight = topapplet.param.piece_h;
        boardstartx = piecewidth+10;
        boardstarty = pieceheight+10;
        rownb = topapplet.param.colnb;
        colnb = topapplet.param.rownb;
        panewidth = colnb * piecewidth + 2 * boardstartx;
        paneheight = rownb * pieceheight + 2 * boardstarty;
        incrementalpaint = false;
        // double buffering initialisation
        myimage = topapplet.createImage(panewidth, paneheight);
        buffer = myimage.getGraphics();
        hiddenimage = topapplet.createImage(piecewidth, pieceheight);
        hiddengraphics = hiddenimage.getGraphics();
        setPreferredSize(new Dimension(panewidth, paneheight));
		
	}

    public e2BoardPane(e2Applet in_applet) {
        topapplet = in_applet;
        piecewidth = topapplet.param.piece_w;
        pieceheight = topapplet.param.piece_h;
        boardstartx = piecewidth+10;
        boardstarty = pieceheight+10;
        rownb = topapplet.param.colnb;
        colnb = topapplet.param.rownb;
        panewidth = colnb * piecewidth + 2 * boardstartx;
        paneheight = rownb * pieceheight + 2 * boardstarty;
        incrementalpaint = false;
        // double buffering initialisation
        myimage = topapplet.createImage(panewidth, paneheight);
        buffer = myimage.getGraphics();
        hiddenimage = topapplet.createImage(piecewidth, pieceheight);
        hiddengraphics = hiddenimage.getGraphics();
        setPreferredSize(new Dimension(panewidth, paneheight));
        addMouseListener(this);
		addMouseMotionListener(this);

    }

    public void updateBoard(){
        //paintComponent(buffer);
        repaint();
    }
    
	public void paintFullPane(Graphics g) {
        e2piece temppiece;
        prevmouseX = mouseX;
        prevmouseY = mouseY;
        buffer.setColor(Color.black);
        buffer.fillRect(0, 0, panewidth, paneheight);
        buffer.setColor(Color.gray);
        buffer.fillRect(boardstartx - piecewidth, boardstarty - pieceheight, 2*piecewidth + colnb * piecewidth, 2*pieceheight + rownb * pieceheight);

		//smooth border around the board
        buffer.setColor(Color.white);
        buffer.drawLine(boardstartx-5 + colnb * piecewidth +10  - 1, boardstarty-5 + 1, boardstartx-5 + colnb * piecewidth +10 - 1, boardstarty-5 + rownb * pieceheight +10 - 1);
        buffer.drawLine(boardstartx-5 + colnb * piecewidth +10 - 2, boardstarty-5 + 2, boardstartx-5 + colnb * piecewidth +10 - 2, boardstarty-5 + rownb * pieceheight +10 - 2);
        buffer.drawLine(boardstartx-5 + 1, boardstarty-5 + rownb * pieceheight +10 - 1, boardstartx-5 + colnb * piecewidth +10 - 1, boardstarty-5 + rownb * pieceheight +10 - 1);
        buffer.drawLine(boardstartx-5 + 2, boardstarty-5 + rownb * pieceheight +10 - 2, boardstartx-5 + colnb * piecewidth +10 - 2, boardstarty-5 + rownb * pieceheight +10 - 2);
        buffer.setColor(Color.black);
        buffer.drawLine(boardstartx-5 + 1, boardstarty-5 + 1, boardstartx-5 + 1, boardstarty-5 + rownb * pieceheight +10 - 1);
        buffer.drawLine(boardstartx-5 + 2, boardstarty-5 + 2, boardstartx-5 + 2, boardstarty-5 + rownb * pieceheight +10 - 2);
        buffer.drawLine(boardstartx-5 + 1, boardstarty-5 + 1, boardstartx-5 + colnb * piecewidth +10 - 1, boardstarty-5 + 1);
        buffer.drawLine(boardstartx-5 + 2, boardstarty-5 + 2, boardstartx-5 + colnb * piecewidth +10 - 2, boardstarty-5 + 2);

        buffer.setColor(Color.black);
        buffer.drawLine(boardstartx-3 + colnb * piecewidth +6  - 1, boardstarty-3 + 1, boardstartx-3 + colnb * piecewidth +6 - 1, boardstarty-3 + rownb * pieceheight +6 - 1);
        buffer.drawLine(boardstartx-3 + colnb * piecewidth +6 - 2, boardstarty-3 + 2, boardstartx-3 + colnb * piecewidth +6 - 2, boardstarty-3 + rownb * pieceheight +6 - 2);
        buffer.drawLine(boardstartx-3 + 1, boardstarty-3 + rownb * pieceheight +6 - 1, boardstartx-3 + colnb * piecewidth +6 - 1, boardstarty-3 + rownb * pieceheight +6 - 1);
        buffer.drawLine(boardstartx-3 + 2, boardstarty-3 + rownb * pieceheight +6 - 2, boardstartx-3 + colnb * piecewidth +6 - 2, boardstarty-3 + rownb * pieceheight +6 - 2);
        buffer.setColor(Color.white);
        buffer.drawLine(boardstartx-3 + 1, boardstarty-3 + 1, boardstartx-3 + 1, boardstarty-3 + rownb * pieceheight +6 - 1);
        buffer.drawLine(boardstartx-3 + 2, boardstarty-3 + 2, boardstartx-3 + 2, boardstarty-3 + rownb * pieceheight +6 - 2);
        buffer.drawLine(boardstartx-3 + 1, boardstarty-3 + 1, boardstartx-3 + colnb * piecewidth +6 - 1, boardstarty-3 + 1);
        buffer.drawLine(boardstartx-3 + 2, boardstarty-3 + 2, boardstartx-3 + colnb * piecewidth +6 - 2, boardstarty-3 + 2);

        buffer.setColor(Color.black);
        buffer.drawLine(boardstartx-piecewidth + colnb * piecewidth +2*piecewidth  - 1, boardstarty-pieceheight + 1, boardstartx-piecewidth + colnb * piecewidth +2*piecewidth - 1, boardstarty-pieceheight + rownb * pieceheight +2*pieceheight - 1);
        buffer.drawLine(boardstartx-piecewidth + colnb * piecewidth +2*piecewidth - 2, boardstarty-pieceheight + 2, boardstartx-piecewidth + colnb * piecewidth +2*piecewidth - 2, boardstarty-pieceheight + rownb * pieceheight +2*piecewidth - 2);
        buffer.drawLine(boardstartx-piecewidth + 1, boardstarty-pieceheight + rownb * pieceheight +2*pieceheight - 1, boardstartx-piecewidth + colnb * piecewidth +2*piecewidth - 1, boardstarty-pieceheight + rownb * pieceheight +2*pieceheight - 1);
        buffer.drawLine(boardstartx-piecewidth + 2, boardstarty-pieceheight + rownb * pieceheight +2*pieceheight - 2, boardstartx-piecewidth + colnb * piecewidth +2*piecewidth - 2, boardstarty-pieceheight + rownb * pieceheight +2*pieceheight - 2);
        buffer.setColor(Color.white);
        buffer.drawLine(boardstartx-piecewidth + 1, boardstarty-pieceheight + 1, boardstartx-piecewidth + 1, boardstarty-pieceheight + rownb * pieceheight +2*pieceheight - 1);
        buffer.drawLine(boardstartx-piecewidth + 2, boardstarty-pieceheight + 2, boardstartx-piecewidth + 2, boardstarty-pieceheight + rownb * pieceheight +2*pieceheight - 2);
        buffer.drawLine(boardstartx-piecewidth + 1, boardstarty-pieceheight + 1, boardstartx-piecewidth + colnb * piecewidth +2*piecewidth - 1, boardstarty-pieceheight + 1);
        buffer.drawLine(boardstartx-piecewidth + 2, boardstarty-pieceheight + 2, boardstartx-piecewidth + colnb * piecewidth +2*piecewidth - 2, boardstarty-pieceheight + 2);


		//put letter and number indexes on rows and columns
        
        //for (j = 0; j < colnb; j++) {
        //	buffer.setColor(Color.white);
        //	buffer.drawString("1",boardstartx + j * piecewidth + piecewidth/2 +1 ,boardstarty - pieceheight/2 +1);
        //	buffer.setColor(Color.black);
        //	buffer.drawString("1",boardstartx + j * piecewidth + piecewidth/2 -1 ,boardstarty - pieceheight/2 -1);
        //	buffer.setColor(Color.gray);
        //	buffer.drawString("1",boardstartx + j * piecewidth + piecewidth/2,boardstarty - pieceheight/2);
        //}
        //for (i = 0; i < rownb; i++) {
        //	buffer.setColor(Color.white);
        //	buffer.drawString("1",boardstartx - piecewidth/2 +1,boardstarty + i * pieceheight + pieceheight/2 +1);
        //	buffer.setColor(Color.black);
        //	buffer.drawString("1",boardstartx - piecewidth/2 -1,boardstarty + i * pieceheight + pieceheight/2 -1);
        //	buffer.setColor(Color.gray);
        //	buffer.drawString("1",boardstartx - piecewidth/2,boardstarty + i * pieceheight + pieceheight/2);
        //}
        

		//draw all pieces or empty squares
        for (j = 0; j < colnb; j++) {
            for (i = 0; i < rownb; i++) {
                temppiece = topapplet.game.board.get_piece_at(j, i);
                if (temppiece != null) {
                    buffer.drawImage(topapplet.param.pieceimage[temppiece.id][temppiece.position.rotation][temppiece.hilite], boardstartx + j * piecewidth, boardstarty + i * pieceheight, this);
                } else {
                	temppattern = topapplet.game.get_pattern_at(j,i);
                	if ((temppattern.bottom != 0) || (temppattern.top != 0) || (temppattern.left != 0) || (temppattern.right != 0))  {
                		count = topapplet.game.get_unplaced_matching_pieces_nb(temppattern);
                		if (count == 0) {
                			buffer.setColor(Color.red);
                			buffer.fillRect(boardstartx + j * piecewidth + 1, boardstarty + i * pieceheight + 1,piecewidth-1,pieceheight-1);
                		} else if (count == 1) {
                			buffer.setColor(Color.orange);
                			buffer.fillRect(boardstartx + j * piecewidth + 1, boardstarty + i * pieceheight + 1,piecewidth-1,pieceheight-1);
                		} else if (count == 2) {
                			buffer.setColor(Color.yellow);
                			buffer.fillRect(boardstartx + j * piecewidth + 1, boardstarty + i * pieceheight + 1,piecewidth-1,pieceheight-1);
                			
                		}
                	}
                    buffer.setColor(Color.white);
                    buffer.drawLine(boardstartx + j * piecewidth + piecewidth - 1, boardstarty + i * pieceheight + 1, boardstartx + j * piecewidth + piecewidth - 1, boardstarty + i * pieceheight + pieceheight - 1);
                    buffer.drawLine(boardstartx + j * piecewidth + piecewidth - 2, boardstarty + i * pieceheight + 2, boardstartx + j * piecewidth + piecewidth - 2, boardstarty + i * pieceheight + pieceheight - 2);
                    buffer.drawLine(boardstartx + j * piecewidth + 1, boardstarty + i * pieceheight + pieceheight - 1, boardstartx + j * piecewidth + piecewidth - 1, boardstarty + i * pieceheight + pieceheight - 1);
                    buffer.drawLine(boardstartx + j * piecewidth + 2, boardstarty + i * pieceheight + pieceheight - 2, boardstartx + j * piecewidth + piecewidth - 2, boardstarty + i * pieceheight + pieceheight - 2);
                    buffer.setColor(Color.black);
                    buffer.drawLine(boardstartx + j * piecewidth + 1, boardstarty + i * pieceheight + 1, boardstartx + j * piecewidth + 1, boardstarty + i * pieceheight + pieceheight - 1);
                    buffer.drawLine(boardstartx + j * piecewidth + 2, boardstarty + i * pieceheight + 2, boardstartx + j * piecewidth + 2, boardstarty + i * pieceheight + pieceheight - 2);
                    buffer.drawLine(boardstartx + j * piecewidth + 1, boardstarty + i * pieceheight + 1, boardstartx + j * piecewidth + piecewidth - 1, boardstarty + i * pieceheight + 1);
                    buffer.drawLine(boardstartx + j * piecewidth + 2, boardstarty + i * pieceheight + 2, boardstartx + j * piecewidth + piecewidth - 2, boardstarty + i * pieceheight + 2);
                    if ((topapplet.param.selection == true) && (topapplet.param.selection_col == j) && (topapplet.param.selection_row == i)) {
                    	buffer.setColor(Color.white);
                    	buffer.drawRect(boardstartx + j * piecewidth + 1, boardstarty + i * pieceheight + 1, piecewidth - 2, pieceheight - 2);
                    	buffer.drawRect(boardstartx + j * piecewidth + 2, boardstarty + i * pieceheight + 2, piecewidth - 4, pieceheight - 4);
                    	buffer.drawRect(boardstartx + j * piecewidth + 3, boardstarty + i * pieceheight + 3, piecewidth - 6, pieceheight - 6);
                    }
                }
        		if (topapplet.param.mode_auto == true) {
        			if (topapplet.game.solver.is_position_selected(j,i)) {
                    	buffer.setColor(Color.white);
                    	buffer.drawRect(boardstartx + j * piecewidth + 1, boardstarty + i * pieceheight + 1, piecewidth - 2, pieceheight - 2);
                    	//buffer.drawRect(boardstartx + j * piecewidth + 2, boardstarty + i * pieceheight + 2, piecewidth - 4, pieceheight - 4);
                    	//buffer.drawRect(boardstartx + j * piecewidth + 3, boardstarty + i * pieceheight + 3, piecewidth - 6, pieceheight - 6);
        			}
        		}

            }
        }
        //copy the rectangle under the mouse
        hiddengraphics.drawImage(myimage,0,0,piecewidth,pieceheight,mouseX-piecewidth/2, mouseY-pieceheight/2,mouseX + piecewidth/2, mouseY + pieceheight/2,this);
        if (topapplet.param.drag == true) {
        	buffer.drawImage(topapplet.param.pieceimage[topapplet.param.dragged_piece.id][topapplet.param.dragged_piece.position.rotation][topapplet.param.dragged_piece.hilite],mouseX - (int) topapplet.param.piece_w/2,mouseY - (int) topapplet.param.piece_h/2, this);
        	buffer.setColor(Color.white);
			buffer.drawRect(mouseX - (int) topapplet.param.piece_w/2, mouseY - (int) topapplet.param.piece_h/2, topapplet.param.piece_w - 1, topapplet.param.piece_h - 1);				
        }
        g.drawImage(myimage, 0, 0, this);
	}
	
	public void paintIncrementalPane(Graphics g) {
        e2piece temppiece;
        
		int i,j;
		int mousei,mousej;
		int prevmousei,prevmousej;
        mousej = (int) ((mouseX - boardstartx)/piecewidth);
        mousei = (int) ((mouseY - boardstarty)/pieceheight);
        prevmousej = (int) ((prevmouseX - boardstartx)/piecewidth);
        prevmousei = (int) ((prevmouseY - boardstarty)/pieceheight);

        //draw the rectangle under the previous mouse position 
        buffer.drawImage(hiddenimage, prevmouseX - piecewidth/2, prevmouseY - pieceheight/2,this); 
        prevmouseX = mouseX;
        prevmouseY = mouseY;
        //update around new mouse position
        //copy the rectangle under the new mouse position
        hiddengraphics.drawImage(myimage,0,0,piecewidth,pieceheight,mouseX-piecewidth/2, mouseY-pieceheight/2,mouseX + piecewidth/2, mouseY + pieceheight/2,this);
        if (topapplet.param.drag == true) {
        	buffer.drawImage(topapplet.param.pieceimage[topapplet.param.dragged_piece.id][topapplet.param.dragged_piece.position.rotation][topapplet.param.dragged_piece.hilite],mouseX - (int) topapplet.param.piece_w/2,mouseY - (int) topapplet.param.piece_h/2, this);
        	buffer.setColor(Color.white);
			buffer.drawRect(mouseX - (int) topapplet.param.piece_w/2, mouseY - (int) topapplet.param.piece_h/2, topapplet.param.piece_w - 1, topapplet.param.piece_h - 1);				
        }
		g.drawImage(myimage , 0 , 0 , this);
		
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);

		if ( (incrementalpaint == false) || (topapplet.param.redraw_boardpane == true) ||(topapplet.param.mode_auto == true) ) {
			paintFullPane(g);
			incrementalpaint = true;
            topapplet.param.redraw_boardpane = false;
		} else {
			paintIncrementalPane(g);
		}
	}
	
	public void mousePressed(MouseEvent evt) {
		int x,y;
		int i,j;
		boolean outside;
		e2piece temppiece;
        e2colorpattern temppattern;
        outside = false;
		x = evt.getX(); 
		y = evt.getY();
		if ((x < boardstartx) || (y < boardstarty) || (x > boardstartx + topapplet.param.colnb * topapplet.param.piece_w) || (x > boardstartx + topapplet.param.rownb * topapplet.param.piece_h)) {
			outside = true;
			if( evt.getModifiers() == InputEvent.BUTTON3_MASK ) {
				//right mouse pressed
				if (topapplet.param.drag == true) {
					//piece dragged : rotating
					topapplet.param.dragged_piece.rotate(1);
				}
			} else {
				if (topapplet.param.drag == true) {
            		//piece dragged : sending back to piece bag
         			topapplet.param.drag = false;
        			topapplet.param.dragged_piece = null;
    			    topapplet.param.redraw_piecebag = true;
    			    topapplet.param.redraw_boardpane = true;
                    topapplet.repaint();
				} else {
					//no piece dragged : dehighlighting everything
                    dehighlight_pieces();
                    topapplet.param.selection = false;
    			    topapplet.param.redraw_piecebag = true;
    			    topapplet.param.redraw_boardpane = true;
                    topapplet.repaint();
				}
			}
		} else {
        	j = (int) ((x - boardstartx)/piecewidth);
        	i = (int) ((y - boardstarty)/pieceheight);
			if (topapplet.param.mode_auto == true) {
				//System.out.println(" adding point "+j+" "+i);
				topapplet.game.solver.add_position(j,i);
				topapplet.game.solver.restart_solver();
			} else {
				if( evt.getModifiers() == InputEvent.BUTTON3_MASK ) {
					//System.out.println(" right mouse pressed at "+j+" "+i);
					if (topapplet.param.drag == false) {
						//no piece dragged : looking at board below
						temppiece = topapplet.game.board.get_piece_at(j,i);
						if (temppiece != null) {
							//piece on on board : rotate
							temppiece.rotate(1);
						} else {
							//no piece on board : remove highlighting if any
							dehighlight_pieces();
							topapplet.param.redraw_piecebag = true;
							topapplet.param.redraw_boardpane = true;
							topapplet.repaint();
						}
					} else {
						//piece dragged : rotating
						topapplet.param.dragged_piece.rotate(1);
					}
				} else {
					//System.out.println(" left mouse pressed at "+j+" "+i);
					if (topapplet.param.drag == false) {
						//no piece dragged : looking at board below
						temppiece = topapplet.game.board.get_piece_at(j,i);
						if (temppiece != null) {
							//piece on board : selecting and dragging
							topapplet.param.drag = true;
							topapplet.param.dragged_piece = temppiece;
							topapplet.game.unplace_piece_at(j,i);
						} else {
							//no piece here -> selecting the empty square and highlighting matching pieces
							//System.out.println(" highlighting at "+j+" "+i);
							temppattern = topapplet.game.get_pattern_at(j,i);
							highlight_pieces(temppattern);
							topapplet.param.selection = true;
							topapplet.param.selection_row = i;
							topapplet.param.selection_col = j;
  			      			topapplet.param.redraw_piecebag = true;
  			      			topapplet.param.redraw_boardpane = true;
  			      			topapplet.repaint();
						}        

					} else {
						//piece dragged : placing the piece below if empty square + dehighlighting pieces
						temppiece = topapplet.game.board.get_piece_at(j,i);
						if ((temppiece == null) || ( (temppiece.position.col == j) && (temppiece.position.row == i)) ) {
							topapplet.game.place_piece_at(topapplet.param.dragged_piece.id,j,i,0);
							topapplet.param.dragged_piece.set_position(i,j);
							topapplet.param.drag = false;
							topapplet.param.dragged_piece = null;
							dehighlight_pieces();
							topapplet.param.selection = false;
							topapplet.param.redraw_piecebag = true;
							topapplet.param.redraw_boardpane = true;
							topapplet.repaint();
						} 
        	
					}
				}
			}

        }
        incrementalpaint = false;
        repaint();
	}
    
	public void mouseReleased(MouseEvent evt) {
		int x,y;
		int i,j;
		x = evt.getX(); 
		y = evt.getY();
        j = (int) ((x - boardstartx)/piecewidth);
        i = (int) ((y - boardstarty)/pieceheight);
        //System.out.println(" mouse released at "+j+" "+i);
        incrementalpaint = false;
        repaint();
	}
	public void mouseEntered(MouseEvent evt) {
        incrementalpaint = false;
        repaint();
	}
	public void mouseExited(MouseEvent evt) {
		mouseX = -piecewidth - 1;
		mouseY = -pieceheight - 1;
        incrementalpaint = false;
        repaint();
	}
	public void mouseClicked(MouseEvent evt) {
		int i,j;
		int x,y;
		x = evt.getX(); 
		y = evt.getY();
        incrementalpaint = false;
        j = (int) ((x - boardstartx)/piecewidth);
        i = (int) ((y - boardstarty)/pieceheight);
        System.out.println(" mouse clicked at "+j+" "+i);
        repaint();
	}
	public void mouseDragged(MouseEvent evt) {
		int x,y;
		int i,j;
		boolean outside;
		e2piece temppiece;
        e2colorpattern temppattern;
        outside = false;
		x = evt.getX(); 
		y = evt.getY();
		if ((x < boardstartx) || (y < boardstarty) || (x > boardstartx + topapplet.param.colnb * topapplet.param.piece_w) || (x > boardstartx + topapplet.param.rownb * topapplet.param.piece_h)) {
			outside = true;
		} else {
        	j = (int) ((x - boardstartx)/piecewidth);
        	i = (int) ((y - boardstarty)/pieceheight);
			if (topapplet.param.mode_auto == true) {
				//System.out.println(" adding point "+j+" "+i);
				if (topapplet.game.solver.add_position(j,i)) {
					topapplet.game.solver.restart_solver();
				}
			}
        }
        incrementalpaint = false;
        repaint();
	}
	public void mouseMoved(MouseEvent evt) {
		int x,y;
		mouseX = evt.getX(); 
		mouseY = evt.getY();
        repaint();
	}

	public void highlight_pieces(e2colorpattern in_pattern) {
    	e2piecelist temppiecelist;
        e2piece temppiece;
        temppiecelist = topapplet.game.get_all_pieces();
        temppiecelist.restart();
        while (temppiecelist.hasNext()) {
            temppiece = temppiecelist.next();
        	if (temppiece.is_matching_pattern(in_pattern)) {
            	//System.out.println(" highlighting piece "+temppiece.id);
            	temppiece.highlight();
            } else {
            	temppiece.dehighlight();
            }
        }
	}

	public void dehighlight_pieces() {
    	e2piecelist temppiecelist;
        e2piece temppiece;
        temppiecelist = topapplet.game.get_all_pieces();
        temppiecelist.restart();
        while (temppiecelist.hasNext()) {
            temppiece = temppiecelist.next();
            temppiece.highlight();
        }
	}
    
    
}
